Electrostatics! It is the study of stationary electric charges and their associated electric fields. But what is charge?
Charge is the property of a particle that interacts with an electric field, and a potential function can be defined across space - this is a scalar field, meaning a function that takes a vector in 3D space and returns a scalar value associated with that point.
As you can see, each point in space has a scalar value corresponding to the temperature in this case. A 'contour map' has been drawn showing regions where the scalar value is in a specific range, visually showing the fact that it is hottest in the middle of the circle. This is how an electric potential function works.
An electric field, on the other hand, has a direction and a scalar magnitude assoiated with each point - it is a vector field, so a function that takes in a 3D vector and outputs another 3D vector associated with that point in space. $\renewcommand{\vec}[1]{\mathbf{#1}}$
The electric field of a point charge follows an inverse square law, and is given by $$\vec{E} = \frac{Q}{4\pi\epsilon_0 r^3} \vec{r}$$
Where Q is the charge of the particle, and $\vec{r}$ is the displacement vector from the charge. It is easy to see that positive charges result in a positive divergence at the position of the charge and negative charges result in negative divergence.
The scalar electric potential field is $V$, which satisfies the following condition.
$$\vec{E}=-\nabla V$$Where $\nabla$ is the gradient operator.
In [7]:
import plotly.plotly as py
import plotly.figure_factory as ff
import plotly.graph_objs as go
import plotly as plty
from plotly.offline import iplot
import numpy as np
plty.offline.init_notebook_mode()
The next block defines a point charge class, called particle. It has three properties: $x$ and $y$ to define it's position and $q$ for it's charge.
In [2]:
class particle:
def __init__(self, x, y, charge):
self.x = x
self.y = y
self.q = charge
The next block sets up the variables that define the visualisation. Feel free to alter these!
'fine' defines the density of sampling points. Making it smaller adds more points to the grid with a trade-off for processing times. 'size' is the value reached in all 4 directions of the grid. 'rad' is the radius to exclude around the point particle. As you notice from the electric field equation, the closer you are to the particle the electric field value approaches greater and greater numbers with no bound - for a usable visualisation a finite area around each particle is excluded.
In [3]:
fine = 0.2
size = 4
rad = 0.5
figsize = 800
particles = []
In [4]:
def plotfields(particles):
outside = True
a = np.arange(-1*size, size, fine)
x,y = np.meshgrid(a, a)
#create exclusion condition - exclude a finite area around each point charge so we do not get infinite values arising
for i in range(0, len(particles)):
r = np.sqrt((x-particles[i].x)**2 + (y-particles[i].y)**2)
outside = (outside) & (r>rad)
#use exclusion condition and boolean indexing to remove those points from the meshgrid
x = x[outside]
y = y[outside]
#u and v are the x and y components of the vectors on the field
u = 0
v = 0
#zval is the scalar force field, and xval and yval are the positions of the point charges
zval = 0
xval = []
yval = []
for i in range(0, len(particles)):
p = particles[i]
rx = x - p.x
ry = y - p.y
r = np.sqrt(rx**2+ry**2)
E = p.q/r**2
cos = rx/r
sin = ry/r
u += E*cos
v += E*sin
V = E*r
zval += V
xval.append(p.x)
yval.append(p.y)
# Create quiver figure
vectorfield = ff.create_quiver(x, y, u, v,
scale=.05,
arrow_scale=.4,
name='quiver',
line=dict(width=1))
# Create points
points = go.Scatter(x=xval, y=yval,
mode='markers',
marker=dict(size=12),
name='points')
#Create contour
cont = go.Contour(z=[zval])
# Add points to figure
vectorfield['data'].append(points)
vectorfield['layout'].update(width = figsize, height = figsize,
xaxis = dict(title = 'x'),
yaxis = dict(title = 'y'))
contourdata = [{'x':x, 'y':y, 'z':zval, 'type':'contour'}]
contourlayout = dict(
width=figsize,height=figsize,
showlegend=True,
xaxis = dict(title = 'x'),
yaxis = dict(title = 'y'),
scene = dict(
xaxis = dict(range=[-2, 2], autorange=False, zeroline=False),
yaxis = dict(range=[-2, 2], autorange=False, zeroline=False),
),
plot_bgcolor= 'rgb(255, 255, 255)')
contourdata.append(points)
equipotentials=go.Figure(data=contourdata, layout=contourlayout)
iplot(vectorfield)
iplot(equipotentials)
The following block is where you can adjust the positions and the charges of the particles, and even add or remove more from the particle list!
In [5]:
p1 = particle(-1, 0, 1)
p2 = particle(1, 0, 1)
p3 = particle(0, -3, -3)
p4 = particle(0, 3, -2)
particles = [p1, p2, p3, p4]
In [6]:
plotfields(particles)
Remember to go back and alter the positions and the charges!